package com.mjf.recursion;

/**
 * 8皇后问题
 * <p>
 * 在8×8格的国际象棋上摆放八个皇后，使其不能互相攻击。即：任意两个皇后都不能处于同一行、同一列或同一斜线上，问有多少种摆法。
 */
public class Queue8 {

    /**
     * 表示共有多少个皇后
     */
    private static final int max = 8;
    /**
     * 保存皇后放置位置的结果
     * 皇后所在行（下标+1），皇后所在列（值+1）
     * 比如：arr = {0,4,7,5,2,6,1,3}
     */
    private static final int[] array = new int[max];
    /**
     * 记录可以正确摆放8皇后的次数
     */
    private static int count = 0;

    public static void main(String[] args) {
        putQueue(0);    // 放置第1个皇后，必须从第一个开始
        System.out.printf("总共有%d种摆放方式", count);
    }

    /**
     * 放置n+1个皇后
     * <p>
     * 思路：
     * 1. 第一个皇后先放第一行第一列
     * 2. 第二个皇后放在第二行第一列，然后判断是否OK，如果不OK，继续放在第二列、第三列、依次把所有列都放完，找到一个合适
     * 3. 继续第三个皇后，还是第一列、第二列……直到第8个皇后也能放在一个不冲突的位置，算是找到了一个正确解
     * 4. 当得到一个正确解时，在栈回退到上一个栈时，就会开始回溯，即将第一个皇后，放到第一列的所有正确解，全部得到.
     * 5. 然后回头继续第一个皇后放第二列，后面继续循环执行1,2,3,4的步骤
     */
    private static void putQueue(int n) {
        if (n == max) {
            // n=8时，说明8个皇后已经放好
            show();
            return;
        }

        for (int i = 0; i < max; i++) { // 位置冲突时会在本行自动后移
            array[n] = i;
            if (check(n)) { // 判断皇后n是否可以放在位置i
                putQueue(n + 1); // 递归放置下一个皇后
            }
        }
    }

    /**
     * 检测第n个皇后是否可以摆放(是否和前面已经摆放的皇后冲突)
     *
     * @param n 第n个皇后
     * @return 是否可以摆放
     */
    private static boolean check(int n) {
        for (int i = 0; i < n; i++) {
            // array[i] == array[n] 说明第i个皇后和第n个皇后在同一列
            // Math.abs(n - i) == Math.abs(array[n] - array[i]) 说明第i个皇后和第n个皇后在同一条斜线
            if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * 输出皇后摆放的位置
     */
    private static void show() {
        count++;
        for (int item : array) {
            System.out.print(item + " ");
        }
        System.out.println();
    }

}
